/**
 * Software License, Version 1.0 Copyright 2003 The Trustees of Indiana
 * University. All rights reserved. Redistribution and use in source and binary
 * forms, with or without modification, are permitted provided that the
 * following conditions are met: 1) All redistributions of source code must
 * retain the above copyright notice, the list of authors in the original source
 * code, this list of conditions and the disclaimer listed in this license; 2)
 * All redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the disclaimer listed in this license in the
 * documentation and/or other materials provided with the distribution; 3) Any
 * documentation included with all redistributions must include the following
 * acknowledgement: "This product includes software developed by the Community
 * Grids Lab. For further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/." Alternatively, this acknowledgement may
 * appear in the software itself, and wherever such third-party acknowledgments
 * normally appear. 4) The name Indiana University or Community Grids Lab or
 * NaradaBrokering, shall not be used to endorse or promote products derived
 * from this software without prior written permission from Indiana University.
 * For written permission, please contact the Advanced Research and Technology
 * Institute ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5)
 * Products derived from this software may not be called NaradaBrokering, nor
 * may Indiana University or Community Grids Lab or NaradaBrokering appear in
 * their name, without prior written permission of ARTI. Indiana University
 * provides no reassurances that the source code provided does not infringe the
 * patent or any other intellectual property rights of any other entity. Indiana
 * University disclaims any liability to any recipient for claims brought by any
 * other entity based on infringement of intellectual property rights or
 * otherwise. LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
 * NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY
 * GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
/**
 * Software License, Version 1.0 Copyright 2003 The Trustees of Indiana
 * University. All rights reserved. Redistribution and use in source and binary
 * forms, with or without modification, are permitted provided that the
 * following conditions are met: 1) All redistributions of source code must
 * retain the above copyright notice, the list of authors in the original source
 * code, this list of conditions and the disclaimer listed in this license; 2)
 * All redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the disclaimer listed in this license in the
 * documentation and/or other materials provided with the distribution; 3) Any
 * documentation included with all redistributions must include the following
 * acknowledgement: "This product includes software developed by the Community
 * Grids Lab. For further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/." Alternatively, this acknowledgement may
 * appear in the software itself, and wherever such third-party acknowledgments
 * normally appear. 4) The name Indiana University or Community Grids Lab or
 * NaradaBrokering, shall not be used to endorse or promote products derived
 * from this software without prior written permission from Indiana University.
 * For written permission, please contact the Advanced Research and Technology
 * Institute ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5)
 * Products derived from this software may not be called NaradaBrokering, nor
 * may Indiana University or Community Grids Lab or NaradaBrokering appear in
 * their name, without prior written permission of ARTI. Indiana University
 * provides no reassurances that the source code provided does not infringe the
 * patent or any other intellectual property rights of any other entity. Indiana
 * University disclaims any liability to any recipient for claims brought by any
 * other entity based on infringement of intellectual property rights or
 * otherwise. LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
 * NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY
 * GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
package cgl.narada.transport.ssl;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.security.KeyStore;
import java.security.Security;
import java.util.Properties;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;

import org.apache.log4j.Logger;

import cgl.narada.transport.TransportException;
import cgl.narada.transport.sslHttpBase.Transport;
import cgl.narada.transport.sslHttpBase.TransportFactory;
import cgl.narada.util.proxy.WinProxyDetection;

/**
 * @author John Yin, Hongbin Liu
 * @author Harshawardhan Gadgil
 * @version 1.1
 */

public class SSLTransportFactoryJSSEImpl implements TransportFactory {

	final static Logger log = Logger
			.getLogger(SSLTransportFactoryJSSEImpl.class.getName());

	private SSLServerSocket m_serverSocket;
	private Properties m_defaultProperties = new Properties();
	private int m_port = 0; // SSLTransport.DEFAULT_SSL_PORT;
	// private SSLServerSocketFactory m_serverSocketFactory = null;

	private ControlServer controlServer = null;

	// private int certPort = 0;

	/**
	 * -----------------------------------------------------------------
	 */
	public Transport createTransportInstance() {
		// Currently just use JSSE implementation until WinINET is ready.
		Transport t = new SSLTransportJSSEImpl();
		// t.setProperties(m_defaultProperties);
		return t;
	}

	/**
	 * -----------------------------------------------------------------
	 * Parameters: listenerport - The port to listen on truststore - Location of
	 * the trusted authorities database keystore - Location of the
	 * public/private key database truststorePassword - Password to the
	 * truststore keystorePassword - Password to the keystore username - Proxy
	 * authenticated user name. password - Proxy authenticated password.
	 * https.proxyHost - The location of the HTTPS proxy. Will try to auto
	 * detect from System properties if this does not exist. https.proxyPort -
	 * The location of the HTTPS proxy port. Will try to auto detect from System
	 * properties if this does not exist. secure - true | false. If false, will
	 * not do any real SSL.
	 */
	public void setProperties(Properties properties) {
		m_defaultProperties.putAll(properties);
		String val = properties.getProperty("acceptor.ssl.port");
		if (val != null && val.length() > 0) {
			try {
				m_port = Integer.parseInt(val);
			}
			catch (NumberFormatException e) {
				log.error("Invalid listening port " + val
						+ " defaulting to port " + m_port);
			}
		}
		log.debug("Will listen on port " + m_port);

		val = properties.getProperty("acceptor.keyStore");
		if (val != null && val.length() > 0) {
			System.setProperty("javax.net.ssl.keyStore", val);
			log.debug("Setting keyStore to " + val);
		}

		val = properties.getProperty("acceptor.trustStore");
		if (val != null && val.length() > 0) {
			System.setProperty("javax.net.ssl.trustStore", val);
			log.debug("Setting truststore to " + val);
		}

		val = properties.getProperty("acceptor.keyStorePassword");
		if (val != null && val.length() > 0) {
			System.setProperty("javax.net.ssl.keyStorePassword", val);
			log.debug("[for debugging purpose] Setting keyStore password to "
					+ val);
		}

		val = properties.getProperty("acceptor.trustStorePassword");
		if (val != null && val.length() > 0) {
			System.setProperty("javax.net.ssl.trustStorePassword", val);
			log.debug("[for debugging purpose] Setting TrustStore password to "
					+ val);
		}

		/**
		 * if factory is for server side, do we need this here? val =
		 * properties.getProperty("trustStorePassword"); if (val != null &&
		 * val.length() > 0) {
		 * System.getProperties().put("javax.net.ssl.trustStorePassword",val);
		 * log.debug("Setting trustStore password to "+val); } val =
		 * properties.getProperty("trustStore"); if (val != null && val.length() >
		 * 0) { System.getProperties().put("javax.net.ssl.trustStore",val);
		 * log.debug("Setting trustStore to "+val); }
		 */

		// HG: Added to start control server only when specifically requested
		val = properties.getProperty("controlServer.port");
		if (val != null && val.length() > 0) {
			try {
				m_port = 0;
				m_port = Integer.parseInt(val);
			}
			catch (NumberFormatException e) {
				log.error("Invalid SSL Cerfiticate sender Control Server port "
						+ val + " defaulting to port " + m_port);
			}
			log.debug("Will start Control server on port " + m_port);

			makeSSLKeys();

			try {
				controlServer = new ControlServer(m_port);
				controlServer.start();
			}
			catch (SSLControlException e) {
				log.warn("Unable to start control server");
				log.error("", e);
			}
		}
		//
		// m_serverSocketFactory = (SSLServerSocketFactory)
		// SSLServerSocketFactory
		// .getDefault();

		log.debug("Set to secure mode");
	}

	/**
	 * ----------------------------------------------------------------- Starts
	 * a listening socket.
	 */
	public void initialize() throws TransportException {

		// HG: Modified to add ssl support

		// Make sure that JSSE is available
		Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());

		SSLContext sslc = null;
		try {
			// A keystore is where keys and certificates are kept
			// Both the keystore and individual private keys should be password
			// protected
			KeyStore keystore = KeyStore.getInstance("JKS");
			SSLJSSEControl sjc = SSLJSSEControl.getInstance();

			// If this is a client then no need to start SSL Server socket
			if (sjc.keyStoreFile() != null) {

				keystore.load(new FileInputStream(sjc.keyStoreFile()), sjc
						.keyStorePass().toCharArray());

				// A KeyManagerFactory is used to create key managers
				KeyManagerFactory kmf = KeyManagerFactory
						.getInstance("SunX509");

				// Initialize the KeyManagerFactory to work with our keystore
				kmf.init(keystore, sjc.keyStorePass().toCharArray());

				// An SSLContext is an environment for implementing JSSE
				// It is used to create a ServerSocketFactory
				sslc = SSLContext.getInstance("SSLv3");

				// Initialize the SSLContext to work with our key managers
				sslc.init(kmf.getKeyManagers(), null, null);

				// Create a ServerSocketFactory from the SSLContext
				ServerSocketFactory ssf = sslc.getServerSocketFactory();

				// Socket to me
				// SSLServerSocket serverSocket = (SSLServerSocket) ssf
				// .createServerSocket(serverPort);

				// Return a ServerSocket on the desired port (443)

				try {
					// if (m_serverSocketFactory == null) {
					// m_serverSocket = new ServerSocket(m_port);
					// }
					// else {
					log.debug("initialize starts");
					log.debug("javax.net.ssl.keyStore="
							+ System.getProperty("javax.net.ssl.keyStore"));
					log
							.debug("javax.net.ssl.keyStorePassword="
									+ System
											.getProperty("javax.net.ssl.keyStorePassword"));

					m_serverSocket = (SSLServerSocket) ssf
							.createServerSocket(m_port);

					// Authenticate the client?
					m_serverSocket.setNeedClientAuth(false);
					log.info("Created SSL Socket on port: " + m_port);
				}
				catch (Exception e) {
					log.error("can't create SSL ServerSocket");
					throw new TransportException("IOException="
							+ e.getMessage());
				}

				log.debug("SSL acceptor created on port " + m_port);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
            throw new TransportException(e.toString());
		}

		String os = System.getProperty("os.name");

		if (os.toLowerCase().indexOf("windows") >= 0) {
			WinProxyDetection detection = new WinProxyDetection();
			String domain = detection.GetDomainWorkgroupName();
			String hostname = detection.GetHostName();
			log.debug("Detected windows domain: " + domain);
			log.debug("Detected windows hostname: " + hostname);
			m_defaultProperties.setProperty("domain", domain);
			m_defaultProperties.setProperty("hostname", hostname);
		}

	}

	/**
	 * -----------------------------------------------------------------
	 */
	public Transport accept() throws TransportException {
		if (m_serverSocket == null) {
			throw new TransportException("server socket is null");
		}
		try {
			Socket socket = m_serverSocket.accept();
			socket.setSoLinger(false, 0);
			socket.setTcpNoDelay(true);
			return new SSLTransportJSSEImpl(socket);

		}
		catch (IOException e) {
			throw new TransportException("IOException=" + e.getMessage());
		}
	}

	private void makeSSLKeys() {

		try {
			SSLJSSEControl sjc = SSLJSSEControl.getInstance();
			sjc.genKeys();
			sjc.genCert();

			System.setProperty("javax.net.ssl.keyStore", sjc.keyStoreFile());
			System.setProperty("javax.net.ssl.keyStorePassword", sjc
					.keyStorePass());

		}
		catch (Exception e) {
			log.warn("having problem with generating keys");
			e.printStackTrace();
		}
	}

	// private void startCertSending() {
	//
	// new Thread("CertificateSender") {
	// public void run() {
	// try {
	// new ControlServer(certPort).sendCertificate();
	// }
	// catch (Exception e) {
	// e.printStackTrace();
	// }
	// }
	// }.start();
	// }

}
